﻿using System;
using MathNet.Numerics;
using MathNet.Numerics.LinearAlgebra.Double;

namespace Marvin.Categorization.NeuronalNetworks
{
  
    public class Layer
    {
  
        public Layer(int numberOfNeurons, Layer previousLayer = null, Matrix parameters = null)
        {
            NumberOfNeurons = numberOfNeurons; 
            Previous = previousLayer;
            Parameters = parameters; 
        }



        public Layer Previous = null;
        public Layer Next = null;
        public Matrix Parameters = null; 
 
        public Vector Values;

        public int NumberOfNeurons;

        public uint Propagate()
        {
            Activate();

            // For performance reasons, the "is OutputLayer" check is not used
            // the builder guarantees that only the output layer has next == null 
            if (Next == null)
            {
                var outputLayer = (OutputLayer) this;
                return outputLayer.GetResult(); 
            }

            return Next.Propagate();
        }

        private void Activate()
        {   
            Values = (Vector)Parameters.Multiply(Previous.Values);
            Values.MapInplace(SpecialFunctions.Logistic);
        }

        public InputLayer GetInputLayer()
        {
            Layer currentCandidate = this; 
            while (currentCandidate.Previous != null)
            {
                currentCandidate = currentCandidate.Previous; 
            }

            var inputLayer = currentCandidate as InputLayer;

            return inputLayer; 
        }

        public OutputLayer GetOutputLayer()
        {
            Layer currentCandidate = this;
            while (currentCandidate.Next != null)
            {
                currentCandidate = currentCandidate.Next;
            }

            var outputLayer = currentCandidate as OutputLayer;

            return outputLayer;
        }
    }
}
